package com.ungao.framework.bytecode;

import com.ungao.framework.convert.CaseConverter;
import org.objectweb.asm.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 字节码工具ASM
 *
 * @author vic-ungao
 */
public class AsmParameterNameLoader {

    /**
     * 使用字节码工具ASM来获取方法的参数名
     * 主要使用LinkedHashMap来保证参数顺序
     *
     * @param method
     * @return 参数名称::参数修饰信息
     * @throws IOException
     */
    public Map<String, Parameter> getParameterNames(final Method method) throws IOException {
        final String methodName = method.getName();
        final Parameter[] parameters = method.getParameters();
        final int parameterSize = parameters.length;
        final String className = method.getDeclaringClass().getName();
        final boolean isStatic = Modifier.isStatic(method.getModifiers());
        final Map<String, Parameter> parameterNames = new LinkedHashMap<>(parameterSize);

        String classPath = className.replace('.', '/') + ".class";
        InputStream inputStream = AsmParameterNameLoader.class.getClassLoader().getResourceAsStream(classPath);
        new ClassReader(inputStream).accept(new ClassVisitor(Opcodes.ASM7) {

            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

                //获取方法参数类型数组信息,比较方法参数类型是否一致
                //为什么要比较? 因为方法的重载
                final Type[] argTypes = Type.getArgumentTypes(desc);
                if (!methodName.equals(name) || !compare(argTypes, parameters)) {
                    return super.visitMethod(access, name, desc, signature, exceptions);
                }

                //静态方法，第一个参数就是方法参数，非静态方法，则第一个参数是this ,然后才是方法的参数
                return new MethodVisitor(Opcodes.ASM7) {

                    @Override
                    public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
                        int parameterIndex = isStatic ? index : index - 1;
                        if (0 <= parameterIndex && parameterIndex < parameterSize) {
                            parameterNames.putIfAbsent(name, parameters[parameterIndex]);
                        }
                        super.visitLocalVariable(name, desc, signature, start, end, index);
                    }
                };
            }
        }, 0);
        return parameterNames;
    }

    /**
     * 比较参数是否一致
     *
     * @param localTypes
     * @param parameterTypes
     * @return
     */
    private boolean compare(Type[] localTypes, Parameter[] parameterTypes) {
        if (localTypes.length != parameterTypes.length) {
            return false;
        }

        for (int i = 0, length = localTypes.length; i < length; i++) {
            Type classType = Type.getType(parameterTypes[i].getType());
            Type localType = localTypes[i];
            if (!classType.equals(localType)) {
                return false;
            }
        }
        return true;
    }
}
